package com.xhtt.modules.inspection.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xhtt.common.exception.RRExceptionEnum;
import com.xhtt.common.utils.DateUtils;
import com.xhtt.common.utils.DoubleInfo;
import com.xhtt.common.utils.EnumUtils;
import com.xhtt.common.utils.ListUtils;
import com.xhtt.modules.inspection.dao.InsClassesMapper;
import com.xhtt.modules.inspection.dto.InsClassesDTO;
import com.xhtt.modules.inspection.dto.InsClassesDetailDTO;
import com.xhtt.modules.inspection.entity.*;
import com.xhtt.modules.inspection.enums.ClassesTypeEnum;
import com.xhtt.modules.inspection.enums.InspectTypeEnum;
import com.xhtt.modules.inspection.enums.ShiftTypeEnum;
import com.xhtt.modules.inspection.service.*;
import com.xhtt.modules.inspection.vo.InsClassesDetailVO;
import com.xhtt.modules.sys.entity.SysDeptEntity;
import com.xhtt.modules.sys.entity.SysUserEntity;
import com.xhtt.modules.sys.service.SysDeptService;
import com.xhtt.modules.sys.service.SysUserDeptService;
import com.xhtt.modules.sys.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 巡检-排班管理 服务实现类
 * </p>
 *
 * @author xj
 * @since 2019-01-29
 */
@Service
@Slf4j
public class InsClassesServiceImpl extends ServiceImpl<InsClassesMapper, InsClasses> implements IInsClassesService {

    @Autowired
    private IInsClassesTeamDetailService insClassesTeamDetailService;

    @Autowired
    private IInsTaskService insTaskService;

    @Autowired
    private IInsTaskDateService insTaskDateService;

    @Autowired
    private IInsTaskUserService insTaskUserService;

    @Autowired
    private IInsTaskStepService insTaskStepService;

    @Autowired
    private IInsLineService insLineService;

    @Autowired
    private SysDeptService sysDeptService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysUserDeptService sysUserDeptService;

    private static final String pattern = DateUtils.DATE_PATTERN;

    private static final DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);

    @Transactional
    @Override
    public void addOrUpdate(InsClassesDTO insClassesDTO) {

        // 主表
        InsClasses insClasses = new InsClasses();
        BeanUtils.copyProperties(insClassesDTO, insClasses);
        insClasses.setStartDate(DateUtils.string2LocalDateTime(insClassesDTO.getStartDate(), pattern));
        insClasses.setEndDate(DateUtils.string2LocalDateTime(insClassesDTO.getEndDate(), pattern));
        baseMapper.insert(insClasses);

        Integer classId = insClasses.getId();

        // 获取所有日期
        List<LocalDateTime> dateList = DateUtils
                .getDays(insClassesDTO.getStartDate(), insClassesDTO.getEndDate(), pattern);

        // 两天分为一组
        List<List<LocalDateTime>> dateGroup = ListUtils.fixedGrouping(dateList, 2);

        // 班组一
        List<Integer> group1 = insClassesDTO.getGroup1();

        // 如果只有一个组则复制一份, 正常情况不会出现, 页面会做判断
        if (group1.size() == 1) group1.add(group1.get(0));

        // 班组二
        List<Integer> group2 = insClassesDTO.getGroup2();
        if (group2.size() == 1) group2.add(group2.get(0));

        List<InsClassesTeamDetail> insClassesTeamDetails = new ArrayList<>();
        InsClassesTeamDetail insClassesTeamDetail;

        boolean flag1 = true;
        boolean flag2 = true;

        /*
         * 倒班(四班两倒)
         * 第一天  1组(日班),2组(夜班)
         * 第二天  1组(日班),2组(夜班)
         * 第三天  3组(日班),4组(夜班)
         * 第四天  3组(日班),4组(夜班)
         * 第五天  2组(日班),1组(夜班)
         * 第六天  2组(日班),1组(夜班)
         * 第七天  4组(日班),3组(夜班)
         * 第八天  4组(日班),3组(夜班)
         */

        for (int i = 0; i < dateGroup.size(); i++) {

            if (dateGroup.get(i).size() == 0) {
                break;
            }

            if (i % 2 == 0) {
                insClassesTeamDetail = schedule(dateGroup.get(i).get(0), group1, classId, flag1);
                insClassesTeamDetails.add(insClassesTeamDetail);
                if (dateGroup.get(i).size() > 1) {
                    insClassesTeamDetail = schedule(dateGroup.get(i).get(1), group1, classId, flag1);
                    insClassesTeamDetails.add(insClassesTeamDetail);
                }
                flag1 = !flag1;
            } else {
                insClassesTeamDetail = schedule(dateGroup.get(i).get(0), group2, classId, flag2);
                insClassesTeamDetails.add(insClassesTeamDetail);
                if (dateGroup.get(i).size() > 1) {
                    insClassesTeamDetail = schedule(dateGroup.get(i).get(1), group2, classId, flag2);
                    insClassesTeamDetails.add(insClassesTeamDetail);
                }
                flag2 = !flag2;
            }
        }
        List<LocalDateTime> dates = insClassesTeamDetails.stream().map(InsClassesTeamDetail::getDate).collect(Collectors.toList());

        // 如果该日期已有排班则覆盖之前的排班
        if (CollectionUtils.isNotEmpty(dates)) {

            // 若排班发生变化,则删除任务时间段包含该日期的所有任务
            dates.forEach(date -> {
                List<Integer> taskIds = baseMapper.taskIdList(date);
                if (CollectionUtils.isNotEmpty(taskIds)) {
                    insTaskService.delete(taskIds);
                }
            });
            insClassesTeamDetailService.remove(new LambdaQueryWrapper<InsClassesTeamDetail>().in(InsClassesTeamDetail::getDate, dates));
        }
        insClassesTeamDetailService.saveBatch(insClassesTeamDetails);
    }


    // 排班
    private InsClassesTeamDetail schedule(LocalDateTime date, List<Integer> group, Integer classId, boolean flag) {

        InsClassesTeamDetail insClassesTeamDetail = group(date, classId);

        if (flag) {
            insClassesTeamDetail.setTeamMorning(group.get(0));
            insClassesTeamDetail.setTeamNight(group.get(1));
        } else {
            insClassesTeamDetail.setTeamMorning(group.get(1));
            insClassesTeamDetail.setTeamNight(group.get(0));
        }
        return insClassesTeamDetail;
    }

    @Override
    public List<InsClassesDetailDTO> getAll(String month) {

        List<InsClassesDetailDTO> insClassesDetailDTOS = new ArrayList<>();

        // 当月已排班对象
        List<InsClassesTeamDetail> insClassesTeamDetails = insClassesTeamDetailService.queryClassesByMonth(month);

        insClassesTeamDetails.forEach(insTaskDate -> {

            InsClassesDetailDTO insClassesDetailDTO = new InsClassesDetailDTO();

            insClassesDetailDTO.setDate(df.format(insTaskDate.getDate()));

            // 设置早班组名称
            SysDeptEntity sysDeptEntity = sysDeptService.getById(insTaskDate.getTeamMorning());
            if (sysDeptEntity != null) {
                insClassesDetailDTO.setMorningTeam(sysDeptEntity.getName());
            }

            // 设置晚班组名称
            SysDeptEntity sysDeptEntity1 = sysDeptService.getById(insTaskDate.getTeamNight());
            if (sysDeptEntity1 != null) {
                insClassesDetailDTO.setNightTeam(sysDeptEntity1.getName());
            }

            insClassesDetailDTOS.add(insClassesDetailDTO);

        });

        return insClassesDetailDTOS;
    }

    @Override
    public DoubleInfo<List<InsClassesDetailVO>, List<InsClassesDetailVO>> getDetail(String date) {

        if (date.length() == 10) {
            date = date + " 00:00:00";
        }

        DoubleInfo<List<InsClassesDetailVO>, List<InsClassesDetailVO>> doubleInfo = new DoubleInfo<>();
        List<InsClassesDetailVO> insClassesDetailVOSMorn = new ArrayList<>();
        List<InsClassesDetailVO> insClassesDetailVOSNight = new ArrayList<>();
        InsClassesDetailVO insClassesDetailVO;

        // 时间
        LocalDateTime dateTime = DateUtils.string2LocalDateTime(date, DateUtils.DATE_TIME_PATTERN);

        // 获取早晚班巡检班组名称
        InsClassesTeamDetail insClassesTeamDetail = insClassesTeamDetailService
                .getOne(new LambdaQueryWrapper<InsClassesTeamDetail>()
                        .eq(InsClassesTeamDetail::getDate, dateTime));

        if (insClassesTeamDetail != null) {

            // 设置排班类型
            InsClasses insClasses = baseMapper.selectById(insClassesTeamDetail.getClassId());

            List<InsTaskDate> insTaskDates = insTaskDateService
                    .list(new LambdaQueryWrapper<InsTaskDate>()
                            .eq(InsTaskDate::getDateTime, dateTime));

            DateTimeFormatter tf = DateTimeFormatter.ofPattern("HH:mm");

            // 设置早晚班巡检时间段
            if (CollectionUtils.isNotEmpty(insTaskDates)) {

                Integer morningDeptId = insClassesTeamDetail.getTeamMorning();

                Integer nightDeptId = insClassesTeamDetail.getTeamNight();

                for (InsTaskDate insTaskDate : insTaskDates) {

                    insClassesDetailVO = new InsClassesDetailVO();

                    insClassesDetailVO.setClassesType(EnumUtils.getMsgByCode(ClassesTypeEnum.class, insClasses.getType()).getMsg());

                    Integer taskId = insTaskDate.getTaskId();

                    InsTask insTask = insTaskService.getAllById(taskId);

                    if (insTask != null && insTask.getInspectType().equals(InspectTypeEnum.DAY.getCode())) {
                        SysDeptEntity dept = sysUserDeptService.getDeptIsGroupByUserId(insTask.getCreateUserId());

                        if (dept != null) {

                            // 查询当天的一条任务
                            InsTaskStep insTaskStep = insTaskStepService
                                    .getOne(new LambdaQueryWrapper<InsTaskStep>()
                                            .eq(InsTaskStep::getTaskId, taskId)
                                            .eq(InsTaskStep::getTaskStartDate, dateTime)
                                            .eq(morningDeptId.equals(dept.getDeptId()), InsTaskStep::getShiftType, ShiftTypeEnum.DAY.getCode())
                                            .eq(nightDeptId.equals(dept.getDeptId()), InsTaskStep::getShiftType, ShiftTypeEnum.NIGHT.getCode())
                                            .last("limit 1"));

                            if (insTaskStep != null) {
                                Integer taskStepId = insTaskStep.getId();

                                // 查询今天所有的任务（step相同），主要是防止有跨天情况出现
//                                List<InsTaskStep> insTaskSteps = insTaskStepService.list(new LambdaQueryWrapper<InsTaskStep>().eq(InsTaskStep::getStep, insTaskStep.getStep()));

//                                if (CollectionUtils.isNotEmpty(insTaskSteps)) {

                                // 巡检时间段, 取最小和最大
                                // 现在任务子表也会存巡检时间段,可以直接获取

//                                    InsTaskStep start = insTaskSteps.stream().sorted(Comparator.comparing(morningDeptId.equals(dept.getDeptId()) ? InsTaskStep::getInspectMorningStartDate : InsTaskStep::getInspectNightStartDate)).limit(1).collect(Collectors.toList()).get(0);
//                                    InsTaskStep end = insTaskSteps.stream().sorted(Comparator.comparing(morningDeptId.equals(dept.getDeptId()) ? InsTaskStep::getInspectMorningEndDate : InsTaskStep::getInspectNightEndDate).reversed()).limit(1).collect(Collectors.toList()).get(0);

                                List<InsTaskUser> insTaskUsers = insTaskUserService
                                        .list(new LambdaQueryWrapper<InsTaskUser>()
                                                .eq(InsTaskUser::getTaskStepId, taskStepId));

                                // 巡检人员
                                if (CollectionUtils.isNotEmpty(insTaskUsers)) {
                                    insClassesDetailVO.setUsername(setUsername(insTaskUsers));
                                }

                                Integer rate = insTaskStep.getInspectRate();

                                // 早班数据
                                if (morningDeptId.equals(dept.getDeptId())) {
                                    SysDeptEntity sysDeptEntity = sysDeptService.getById(morningDeptId);
                                    if (sysDeptEntity != null) {
                                        insClassesDetailVO.setTeamName(sysDeptEntity.getName());
                                    }
                                    // 设置早班时间段
                                    insClassesDetailVO.setStartTime(tf.format(insTaskStep.getMorningStartDate()));

                                    insClassesDetailVO.setEndTime(tf.format(insTaskStep.getMorningEndDate()));

                                    // 设置巡检类型
                                    insClassesDetailVO.setInspectType(EnumUtils.getMsgByCode(InspectTypeEnum.class, insTask.getInspectType()).getMsg());

                                    // 设置巡检频率
                                    insTask.setInspectRate(rate);
                                    insClassesDetailVO.setInspectRate(setRate(insTask));

                                    // 设置线路名称
                                    InsLine insLine = insLineService.getAllById(insTask.getLineId());
                                    if (insLine != null) {
                                        insClassesDetailVO.setLineName(insLine.getName());
                                    }

                                    if (insTask.getInspectType() == 1) {
                                        insClassesDetailVOSMorn.add(insClassesDetailVO);
                                    }
                                }

                                // 晚班数据
                                if (nightDeptId.equals(dept.getDeptId())) {

                                    SysDeptEntity sysDeptEntity1 = sysDeptService.getById(nightDeptId);
                                    if (sysDeptEntity1 != null) {
                                        insClassesDetailVO.setTeamName(sysDeptEntity1.getName());
                                    }

                                    // 设置早班时间段
                                    insClassesDetailVO.setStartTime(tf.format(insTaskStep.getNightStartDate()));

                                    insClassesDetailVO.setEndTime(tf.format(insTaskStep.getNightEndDate()));

                                    // 设置巡检类型
                                    insClassesDetailVO.setInspectType(EnumUtils.getMsgByCode(InspectTypeEnum.class, insTask.getInspectType()).getMsg());

                                    // 设置巡检频率
                                    insTask.setInspectRate(rate);
                                    insClassesDetailVO.setInspectRate(setRate(insTask));

                                    // 设置线路名称
                                    InsLine insLine = insLineService.getAllById(insTask.getLineId());
                                    if (insLine != null) {
                                        insClassesDetailVO.setLineName(insLine.getName());
                                    }

                                    if (insTask.getInspectType() == 1) {
                                        insClassesDetailVOSNight.add(insClassesDetailVO);
                                    }
                                }
                            }
                        }
//                        }
                    }
                }
            }

        } else {
            log.error("{}", dateTime, RRExceptionEnum.NOT_SCHEDULING.getMsg());
        }
        doubleInfo.setFirst(insClassesDetailVOSMorn);
        doubleInfo.setSecond(insClassesDetailVOSNight);
        return doubleInfo;
    }

    private String setUsername(List<InsTaskUser> insTaskUsers) {
        List<Integer> userIds = insTaskUsers.stream().map(InsTaskUser::getUserId).collect(Collectors.toList());
        List<SysUserEntity> sysUserEntities = new ArrayList<>(sysUserService.listByIds(userIds));
        return sysUserEntities.stream().map(SysUserEntity::getName).collect(Collectors.joining("、"));
    }

    private String setRate(InsTask insTask) {

        // 获取巡检频率
        Integer rate = insTask.getInspectRate();
        Integer inspectType = insTask.getInspectType();
        String rateStr = null;
        if (inspectType.equals(InspectTypeEnum.DAY.getCode())) {
            rateStr = rate + "时/次";
        } else if (inspectType.equals(InspectTypeEnum.WEEK.getCode())) {
            rateStr = rate + "次/周";
        } else if (inspectType.equals(InspectTypeEnum.MONTH.getCode())) {
            rateStr = rate + "次/月";
        }
        return rateStr;
    }

    @Override
    public List<String> getMorning(String month) {

        List<LocalDateTime> mornings = baseMapper.getTaskMorningDate(month);

        List<String> result = new ArrayList<>();

        mornings.forEach(e -> result.add(df.format(e)));

        return result;
    }

    @Override
    public List<String> getNight(String month) {

        List<LocalDateTime> nights = baseMapper.getTaskNightDate(month);

        List<String> result = new ArrayList<>();

        nights.forEach(e -> result.add(df.format(e)));

        return result;
    }

    private static InsClassesTeamDetail group(LocalDateTime date, Integer classId) {
        InsClassesTeamDetail insClassesTeamDetail = new InsClassesTeamDetail();
        insClassesTeamDetail.setDate(date);
        insClassesTeamDetail.setClassId(classId);
        return insClassesTeamDetail;
    }

}
